package com.mscsoft.judge;

/** 用于判定音符准确度，记录判定、精度、连击分，根据总分返回评级 */
public class Judger {
    // 1.谱面基本信息
    private int theoreticalCombo;// 理论Combo数

    /**
     * 构造判定器
     * 
     * @param bpm              {@code 谱面每分钟节拍数}用于计算判定阈值
     * @param theoreticalCombo {@code 理论Combo数}用于计算连击分
     */
    public Judger(double bpm, int theoreticalCombo) {
        this.theoreticalCombo = theoreticalCombo;
        // 十六分音符在[120.0,160.0]BPM下的音长
        double semiquaver = 15000.0 / Double.min(Double.max(bpm, 120.0), 160.0);
        // 计算判定阈值
        perfectPL = PERFECT_P * semiquaver;
        perfectL = PERFECT * semiquaver;
        goodL = GOOD * semiquaver;
        badL = BAD * semiquaver;
        missL = MISS * semiquaver;
    }

    // 2.音符判定
    // Perfect+阈值 误差0.5个十六分音符音长内 其余同理
    private static final double PERFECT_P = 0.5;
    private static final double PERFECT = 1.0;
    private static final double GOOD = 1.5;
    private static final double BAD = 2.0;
    private static final double MISS = 4.0;
    private static final double FRAME_TIME = 1000 / 60.0;
    private final double perfectPL;
    private final double perfectL;
    private final double goodL;
    public final double badL;
    public final double missL;
    /** 判定计数{Prefect+, Prefect, Good, Bad, Miss} */
    private int[] counts = { 0, 0, 0, 0, 0 };

    /** 根据时间差判定评价并返回判定百分比 */
    private double judge(double offset) {
        offset = Math.abs(offset);
        if (offset <= perfectPL) {// Perfect+ 84%~92%
            counts[0]++;
            return offset <= FRAME_TIME ? 92 : (perfectPL - offset) / (perfectPL - FRAME_TIME) * 8 + 84;
        } else if (offset <= perfectL) {// Perfect 80%
            counts[1]++;
            return 80;
        } else if (offset <= goodL) {// Good 56%~72%
            counts[2]++;
            return (goodL - offset) / (goodL - perfectL) * 16 + 56;
        } else if (offset <= badL) {// Bad 8%~48%
            counts[3]++;
            return (badL - offset) / (badL - goodL) * 40 + 8;
        } else if (offset <= missL) {// Miss 0%
            counts[4]++;
            return 0;
        } else {// Error
            return -1;
        }
    }

    // 3.计分
    // 总得分(100w)=(判定分+精度分)(92w)+连击分(8w)
    private double noteScore = 0;// 判定分+精度分
    private int currentCombo = 0;// 当前的Combo数
    private int maxCombo = 0;// 取得的最大Combo数 用于计算连击分
    private int score = 0;// 总得分

    /**
     * 根据该次判定延迟计分并返回判定百分比
     * 
     * @param offset 打击点和精确节奏点的时间差
     * @return {@code 判定分+精度分}的百分比(0~92)
     */
    public double scoring(double offset) {
        double s = judge(offset);
        if (s < 0) {// Offset Error
            return -1;
        } else if (s < 56) {// Bad or Miss
            maxCombo = Math.max(maxCombo, currentCombo);
            currentCombo = 0;
        } else {// Other
            currentCombo++;
            maxCombo = Math.max(maxCombo, currentCombo);
        }
        noteScore += 10000.0 * s / theoreticalCombo;
        double comboScore = 80000.0 * maxCombo / theoreticalCombo;
        score = (int) Math.ceil(noteScore + comboScore);
        return s;
    }

    /** 判定评价 */
    public final String[] appraise = { "Perfect+", "Perfect", "Good", "Bad", "Miss" };

    /**
     * 根据判定百分比返回判定评价
     * 
     * @param percentage 判定百分比
     * @return {@code 判定评价}{Perfect+,Perfect,Good,Bad,Miss}
     */
    public int getAppraise(double percentage) {
        if (percentage < 0) {// Error
            return -1;
        } else if (percentage >= 84) {// Perfect+
            return 0;
        } else if (percentage >= 80) {// Perfect
            return 1;
        } else if (percentage >= 56) {// Good
            return 2;
        } else if (percentage >= 8) {// Bad
            return 3;
        } else {// Miss
            return 4;
        }
    }

    // 4.总评
    /**
     * 根据总分返回总评
     * 
     * @return {@code Rating}评价由高至低{@code 您 φ V S A B C D F}
     */
    public String getRating() {
        return getRating(this.score);
    }

    public static String getRating(int score) {
        if (score >= 1000000) {
            return "您";
        } else if (score >= 990000) {
            return "φ";
        } else if (score >= 970000) {
            return "V";
        } else if (score >= 940000) {
            return "S";
        } else if (score >= 900000) {
            return "A";
        } else if (score >= 800000) {
            return "B";
        } else if (score >= 700000) {
            return "C";
        } else if (score >= 600000) {
            return "D";
        } else {
            return "F";
        }
    }

    public int getTheoreticalCombo() {
        return theoreticalCombo;
    }

    public int getCurrentCombo() {
        return currentCombo;
    }

    public int getMaxCombo() {
        return maxCombo;
    }

    public int getScore() {
        return score;
    }

    /** get判定计数{Prefect+, Prefect, Good, Bad, Miss} */
    public int[] getCounts() {
        return counts;
    }
}
